#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "shell.h"
#include "xintc.h"
#include "xuartlite.h"
#include "xuartlite_l.h"
#include "xil_exception.h"
#include "xgpio.h"
#include "xil_io.h"
#include "xspi.h"
#include "ch394q.h"
#include "xtmrctr.h"


#define UART_DEVICE_ID          XPAR_UARTLITE_0_DEVICE_ID
#define TMRCTR_DEVICE_ID        XPAR_TMRCTR_0_DEVICE_ID
#define SPI_DEVICE_ID           XPAR_SPI_0_DEVICE_ID

#define UART_INTR_ID            XPAR_INTC_0_UARTLITE_0_VEC_ID
#define TMRCTR_INTR_ID          XPAR_INTC_0_TMRCTR_0_VEC_ID

#define INTC_ID                 XPAR_INTC_0_DEVICE_ID




#define RX_NOEMPTY              XUL_SR_RX_FIFO_VALID_DATA

#define GPIO_OUTPUT_DEVICE_ID   XPAR_GPIO_1_DEVICE_ID
#define GPIO_INPUT_DEVICE_ID    XPAR_GPIO_0_DEVICE_ID
#define CH394Q_INT_CHANNEL      1
#define LED_CHANNEL             1
#define XIL_EXCEPTION_ID_INT    16
#define LED0                    0x0008 // V20
#define LED1                    0x0010 // P17



#define BRAM_BASE_ADDR          XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR

static XIntc Intc;
static XIntc timer_intc;
static XUartLite Uart;
XGpio gpio;
XGpio gpio_int;
XSpi ch394q_spi;
XTmrCtr timer;

void uart_handler(void *CallbackRef);
void shell_write(const char data);
SHELL_TypeDef shell;

void shell_write(const char data);
void uart_handler(void *CallbackRef);
void ps_write_pl(int32_t freq_div0, uint32_t freq_div1);

int SpiInit(uint16_t DeviceId);
void spi_write(void);
uint32_t QueryCH394QInterrupt(void);
void timer_intr_handler(void* instance_ptr);

#define CH349Q_CS               0X0004               //N20
#define CH394Q_RESET            0X0002                //M22
int main()
{

    init_platform();

    SpiInit(SPI_DEVICE_ID);

    XUartLite_Initialize(&Uart, UART_DEVICE_ID);
    shell.write = shell_write;
    shellInit(&shell);

    XIntc_Initialize(&Intc, INTC_ID);
    XIntc_Connect(&Intc, UART_INTR_ID, (XInterruptHandler)uart_handler, &Uart);
    XUartLite_EnableInterrupt(&Uart);
    XIntc_Start(&Intc, XIN_REAL_MODE);
    XIntc_Enable(&Intc, UART_INTR_ID);

    //timer initialization
    #if 1
    XTmrCtr_Initialize(&timer, TMRCTR_DEVICE_ID);
    XTmrCtr_SetOptions(&timer, 0, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION | XTC_DOWN_COUNT_OPTION);
    XTmrCtr_SetResetValue(&timer, 0, 50000000);
    XTmrCtr_SetHandler(&timer, timer_intr_handler, &timer);
    XTmrCtr_Start(&timer, 0);
    XIntc_Initialize(&Intc, INTC_ID);
    XIntc_Connect(&Intc, TMRCTR_INTR_ID, (XInterruptHandler)XTmrCtr_InterruptHandler, &timer);
    XIntc_Start(&Intc, XIN_REAL_MODE);
    XIntc_Enable(&Intc, TMRCTR_INTR_ID);
    #endif

    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XIntc_InterruptHandler, &Intc);
    Xil_ExceptionEnable();

    int gpio_init_status = XGpio_Initialize(&gpio, GPIO_OUTPUT_DEVICE_ID);
    if (gpio_init_status != XST_SUCCESS)
    {
        printf("Gpio Output Initialization Failed\r\n");
        return XST_FAILURE;
    }
    XGpio_SetDataDirection(&gpio, LED_CHANNEL, 0);

    gpio_init_status = XGpio_Initialize(&gpio_int, GPIO_INPUT_DEVICE_ID);
    if (gpio_init_status != XST_SUCCESS)
    {
        printf("Gpio Input Initialization Failed\r\n");
        return XST_FAILURE;
    }
    XGpio_SetDataDirection(&gpio_int, CH394Q_INT_CHANNEL, 1);

    ch394q_tcp_server_init();

    uint32_t while_count = 0;
    while (1)
    {
        // print("hello world\n\r");
        usleep(20000);
        while_count++;
        uint32_t gpio_status;
        if (while_count == 200)
        {
            gpio_status = XGpio_DiscreteRead(&gpio, LED_CHANNEL);
            XGpio_DiscreteWrite(&gpio, LED_CHANNEL, gpio_status | LED1);
        }
        if (while_count == 400)
        {
            gpio_status = XGpio_DiscreteRead(&gpio, LED_CHANNEL);
            XGpio_DiscreteWrite(&gpio, LED_CHANNEL, gpio_status & (~LED1));
            while_count = 0;
            // printf("INT Input: %d \r\n", (int)QueryCH394QInterrupt());
        }
        ps_write_pl(while_count * 3, while_count * 4);
        if (QueryCH394QInterrupt() == 0)
        {
            CH394Q_GlobalInterrupt();
        }
    }
    cleanup_platform();
    return 0;
}

void ps_write_pl(int32_t freq_div0, uint32_t freq_div1)
{
    Xil_Out32(BRAM_BASE_ADDR + 0, 0XFE);
    Xil_Out32(BRAM_BASE_ADDR + 4, 0X01);
    Xil_Out32(BRAM_BASE_ADDR + 8, 0X01);
    Xil_Out32(BRAM_BASE_ADDR + 12, freq_div0);
    Xil_Out32(BRAM_BASE_ADDR + 16, 0X01);
    Xil_Out32(BRAM_BASE_ADDR + 20, freq_div1);
    Xil_Out32(BRAM_BASE_ADDR + 24, 0XEF);
    uint32_t crc = 0xfe + 0x01 + 0x01 + freq_div0 + 0x01 + freq_div1 + 0xEF;
    Xil_Out32(BRAM_BASE_ADDR + 28, crc);
}

void uart_handler(void *CallbackRef)
{
    u8 Read_data;
    u32 isr_status;
    XUartLite *InstancePtr = (XUartLite *)CallbackRef;
    isr_status = XUartLite_ReadReg(InstancePtr->RegBaseAddress, XUL_STATUS_REG_OFFSET);
    if (isr_status & RX_NOEMPTY)
    {
        Read_data = XUartLite_ReadReg(InstancePtr->RegBaseAddress, XUL_RX_FIFO_OFFSET);
        shellHandler(&shell, Read_data);
    }
}

void shell_write(const char data)
{
    // XUartLite_WriteReg(Uart.RegBaseAddress, XUL_TX_FIFO_OFFSET, data);
    outbyte(data);
}

// spi configure
int SpiInit(uint16_t DeviceId)
{

    /**/
    XSpi_Config *ConfigPtr;
    int Status;

    ConfigPtr = XSpi_LookupConfig(DeviceId);
    if (ConfigPtr == NULL)
    {
        return XST_FAILURE;
    }

    Status = XSpi_CfgInitialize(&ch394q_spi, ConfigPtr, ConfigPtr->BaseAddress);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }
    // XSpi_Initialize(&ch394q_spi, DeviceId);
    Status = XSpi_SelfTest(&ch394q_spi);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /*
     * Run loopback test only in case of standard SPI mode.
     */
    if (ch394q_spi.SpiMode != XSP_STANDARD_MODE)
    {
        return XST_SUCCESS;
    }

    Status = XSpi_SetOptions(&ch394q_spi, XSP_MASTER_OPTION | XSP_MANUAL_SSELECT_OPTION); //|XSP_MANUAL_SSELECT_OPTION
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }
    Status = XSpi_SetSlaveSelect(&ch394q_spi, 0x01);
    if (Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }
    XSpi_Start(&ch394q_spi);
    /*
     * Disable Global interrupt to use polled mode operation
     */
    XSpi_IntrGlobalDisable(&ch394q_spi);
    uint32_t ControlReg = XSpi_GetControlReg(&ch394q_spi);
    ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
    XSpi_SetControlReg(&ch394q_spi, ControlReg);

    printf("spi initial success!\r\n");
    return XST_SUCCESS;
}

void spi_write(void)
{
    uint8_t data[256];
    for (uint16_t i = 0; i < 256; i++)
    {
        data[i] = i;
        while ((XSpi_ReadReg(ch394q_spi.BaseAddr, XSP_SR_OFFSET) & XSP_SR_TX_EMPTY_MASK) == 0)
            ; // wait for transmit empty
        XSpi_WriteReg(ch394q_spi.BaseAddr, XSP_DTR_OFFSET, data[i]);
    }
}

uint32_t QueryCH394QInterrupt(void)
{
    return XGpio_DiscreteRead(&gpio_int, CH394Q_INT_CHANNEL);
}



void timer_intr_handler(void* intance_ptr)
{   
    static uint8_t timer_in_count = 0;
    uint32_t gpio_status = XGpio_DiscreteRead(&gpio, LED_CHANNEL);      
    switch (timer_in_count)
    {
    case 0:
        XGpio_DiscreteWrite(&gpio, LED_CHANNEL, gpio_status | LED0);
        timer_in_count++;
        break;
    case 1:
        XGpio_DiscreteWrite(&gpio, LED_CHANNEL, gpio_status & (~LED0));
        timer_in_count = 0;
        break;
    default:
        break;
    }
}

